package com.onpositive.notes.templates;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.onpositive.collections.ICollectionDelta;
import com.onpositive.collections.IObjectCollection;
import com.onpositive.notes.argumnets.IArgumentsSet;
import com.onpositive.notes.impl.AbstractEntityWithClass;
import com.onpositive.notes.model.IArgumentModel;
import com.onpositive.notes.model.IArgumentValidator;
import com.onpositive.notes.model.ITemplateArgument;
import com.onpositive.notes.model.ITemplateModel;
import com.onpositive.notes.services.BasicCollectionRegistry;
import com.onpositive.notes.services.ITemplateExecutor;
import com.onpositive.notes.services.TemplateModelRegistry;

public class BasicTemplateModel
					  extends AbstractEntityWithClass<BasicTemplate> implements ITemplateModel<BasicTemplate> {

	public BasicTemplateModel() {
		super();
	}

	private static final long serialVersionUID = 7777729402759854170L;

	public static final String INIT_ERROR_MESSAGE = "Can not accept null in constructor if the corresponding init method is not overridden";

	private ArrayList<IArgumentModel<?,?>> argModels ;
	private Map<Locale,ArrayList<String>> bodyMap ;
	private String id ;
	private Map<Locale,String> keyMap ;
	private ArrayList<ITemplateExecutor> executors ;
	private ArrayList<IArgumentValidator> validators = null ;
	private int argSetsCount = -1 ;
	
	
	public BasicTemplateModel(
			String id,
			Map<Locale,String> keyMap,
			Map<Locale,ArrayList<String>> bodyMap,
			ArrayList<IArgumentModel<?,?>> argModels,
			ArrayList<ITemplateExecutor> executors,
			int argSetsCount
			)
	{
		super(BasicTemplate.class);

		this.id = id ;
		this.bodyMap = bodyMap ;		
		this.keyMap = keyMap ;
		this.argModels = argModels ;
		this.executors = executors ;
		this.argSetsCount = argSetsCount ;
		init();		
	}
	
	public BasicTemplateModel(
			String id,
			Map<Locale,String> keyMap,
			Map<Locale,ArrayList<String>> bodyMap,
			ArrayList<IArgumentModel<?,?>> argModels,
			ArrayList<ITemplateExecutor> executors,
			int argSetsCount,
			ArrayList<IArgumentValidator> validators		
			)
	{
		this( id, keyMap, bodyMap, argModels, executors, argSetsCount ) ;
		this.validators = validators ;
	}

	public void init() {
		for(int i = 0 ; i < this.argModels.size() ; i++)
			this.argModels.get(i).setId(i) ;
	}

	@Override
	public String getText(BasicTemplate template) {
		return joinText(template, Locale.getDefault() ) ;
	}
	
	@Override
	public String getText(BasicTemplate template, Locale locale) {
		return joinText(template, locale) ;
	}
	
	@Override
	public String getId(){
		return id ;
	}
	
	@Override
	public List<String> getAllKeys(){
		return new ArrayList<String>( keyMap.values() ) ;
	}
	
	@Override
	public String getKey(){
		return keyMap.get( Locale.getDefault() ) ;
	}
	
	@Override
	public String getKey(Locale locale){
		return keyMap.get(locale) ;
	}
	
	@Override
	public List<IArgumentModel<?,?>> getArgumentModels() {
		return argModels ;
	}
	
	@Override
	public BasicTemplate createInstance()
	{
		//ArrayList<ITemplateArgument<?>> args = initArguments() ;
		BasicTemplate result = new BasicTemplate(/*args,*/ this.getId() );
		TemplateModelRegistry.getInstance().getTemplates( getId() ).add(result) ;
		return result ;
	}
	
	protected String joinText(BasicTemplate template, Locale locale) {
		
		StringBuffer buf = new StringBuffer() ;
		for( IArgumentsSet set: template.getArgSets() ){
			List<ITemplateArgument<?>> args = set.getArgs() ;
			
			ArrayList<String> body = bodyMap.get(locale) ;
			if( body == null )
				body = new ArrayList<String>() ;
			
			int bSize = body.size();
			int aSize = args.size();
			int size = Math.min(aSize, bSize) ;
			
			
			buf.append( keyMap.get(locale)) ;		
			int i = 0 ;
			
			for(  ; i < size ; i++ ){			
				buf.append( body.get(i) ) ;
			
				ITemplateArgument<?> arg = args.get(i);
				String label = arg != null ? arg.getLabel(locale) : "NULL";
				buf.append(" ").append( label ) ;
			}
			
			for(  ; i < bSize ; i++ ){			
				buf.append( body.get(i) ) ;
			}
			
			for(  ; i < aSize ; i++ ){			
				ITemplateArgument<?> arg = args.get(i);
				String label = arg != null ? arg.getLabel(locale) : "NULL";
				buf.append(" ").append( label ) ;
			}
			buf.append("; ") ;
		}		
		return buf.toString() ;
	}
	
	@Override
	public List<ITemplateArgument<?>> initArguments() {
		
		ArrayList<ITemplateArgument<?>> result = new ArrayList<ITemplateArgument<?>>() ;
		for( IArgumentModel<?,?> model : getArgumentModels() ){
			ITemplateArgument<?> arg = model.createInstance(null,this) ;
			result.add(arg) ;
		}
		return result;
	}

	@Override
	public List<Locale> getAvailableLocales() {
		return new ArrayList<Locale>( keyMap.keySet() ) ;
	}
	
	@Override
	public void applyTemplate(BasicTemplate template) {
		
		List<Integer> modificationData = template.getModificationData();
		modificationData.clear() ;
		
		for( ITemplateExecutor exec : executors ){
			ICollectionDelta<?> delta = exec.execute(template);
			modificationData.add( delta.getCollectionId() ) ;
			modificationData.add( delta.getId() ) ;
		}		
	}

	@Override
	public void cancelTemplate(BasicTemplate template) {
		
		List<Integer> modificationData = template.getModificationData() ;
		int size = modificationData.size();
		for( int i = 0 ; i < size ; i += 2 ){				
			int collectionId = modificationData.get(i) ;
			int deltaId = modificationData.get(i+1) ;
			IObjectCollection collection = BasicCollectionRegistry.getInstance().getCollection(collectionId);
			collection.removeDelta(deltaId) ;
		}
		modificationData.clear() ;
	}
	
	public String toString(){
		return this.getId() ;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		BasicTemplateModel other = (BasicTemplateModel) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public IArgumentModel<?, ?> getArgumentModel(int id) {
		return argModels.get(id) ;
	}

	@Override
	public boolean validate(IArgumentModel<?, ?> argModel, Object value) {
		
		if( validators == null )
			return true ;
		
		int ind = argModel.getId();
		if( ind > validators.size() )
			return true ;
		
		IArgumentValidator validator = validators.get(ind);
		if( validator == null )
			return true ;
		
		return validator.validate(value) ;
	}

	@Override
	public int getArgSetsCount() {
		return argSetsCount ;
	}
	
}
